<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PostMe 在线接口调试文档 - Ver2.0.0-beta.220217</title>
    <!-- 说明：断网的情况下，下载依赖的资源文件到本地并引入就可以正常使用了。依赖插件：bootstrap、vue、md5 -->
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet"
        title="">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.26/vue.global.prod.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js" title="参数签名依赖 md5 方法"></script>

    <style>
        html {
            font-size: 14px;
            color: #333;
        }

        div a {
            text-decoration: none;
        }

        .navbar-brand a,
        .navbar-nav .dropdown-item {
            color: #333;
        }

        .aside-menu {
            height: calc(100vh - 50px);
            overflow: auto;
        }

        .aside-menu,
        .aside-menu a {
            color: #555;
        }

        .aside-menu .accordion-button {
            font-weight: bold;
            padding: 0.6rem 1rem;
        }

        .aside-menu .accordion-button:not(.collapsed) {
            color: #333;
            background-color: #f8f9fa;
            box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.125);
        }

        .aside-menu .accordion-button::after {
            color: #333;
            content: "≡";
            background-image: none !important;
        }

        .aside-menu .accordion-button:not(.collapsed)::after {
            transform: none;
        }

        .aside-menu .list-group-item {
            padding: 0.5rem 1rem;
        }

        main {
            height: calc(100vh - 60px);
        }

        .table> :not(:first-child) {
            border-top: none !important;
        }

        th {
            white-space: nowrap !important;
            padding: 0.6rem 0.5rem !important;
        }

        td {
            padding: 0.2rem 0.5rem !important;
            font-size: 13px;
            line-height: 21px;
            vertical-align: middle;
            color: #333 !important;
        }

        h6 span {
            border-bottom: solid 2px #0d6efd;
            padding-bottom: .5rem;
            display: inline-block;
            font-weight: bold;
            color: #555;
        }

        /* 骨架屏 */
        .skeleton {
            position: relative;
            overflow: hidden;
            pointer-events: none;
        }

        .skeleton:after {
            content: "";
            position: absolute;
            left: 0;
            top: 0;
            z-index: 9;
            width: 100%;
            height: 100%;
            background-color: #f8f9fa;
            display: block;
            border-radius: 2px;
        }
    </style>
</head>

<body>
    <div id="app">
        <header class="skeleton">
            <nav class="navbar navbar-expand-lg navbar-light bg-light">
                <div class="container-fluid">
                    <a name="top" class="navbar-brand" href="#">API 在线调试文档</a>
                    <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
                        data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                        <span class="navbar-toggler-icon"></span>
                    </button>
                    <div class="collapse navbar-collapse" id="navbarSupportedContent">
                        <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                            <li class="nav-item">
                                <a class="nav-link active" href="?home">首页</a>
                            </li>
                            <li class="nav-item dropdown">
                                <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
                                    data-bs-toggle="dropdown" aria-expanded="false">
                                    推荐工具
                                </a>
                                <ul class="dropdown-menu">
                                    <li v-for="(item,idx) of olrequestSites" :key="idx">
                                        <a class="dropdown-item" :href="item.url" target="_blank">{{item.text}}</a>
                                    </li>
                                    <li>
                                        <hr class="dropdown-divider">
                                    </li>
                                    <li v-for="(item,idx) of oljsonSites" :key="idx">
                                        <a class="dropdown-item" :href="item.url" target="_blank">{{item.text}}</a>
                                    </li>
                                </ul>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link" href="https://gitee.com/sochishun/postme" target="_blank"
                                    title="源码仓库">Gitee</a>
                            </li>
                        </ul>
                        <form class="d-flex">
                            <div class="input-group input-group-sm">
                                <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"
                                    data-bs-toggle="dropdown">选择服务器</button>
                                <ul class="dropdown-menu">
                                    <li v-for="(item,idx) of servers"><a class="dropdown-item" :href="'#'+item.name"
                                            @click.prevent="onChangeServer(item.url)">{{item.text}}</a></li>
                                </ul>
                                <input class="form-control form-control-sm" type="search" placeholder="服务器网址（必填）"
                                    v-model="baseUrl" style="width:20rem">
                            </div>
                        </form>
                    </div>
                </div>
            </nav>
        </header>
        <div class="container-fluid">
            <div class="row">
                <aside class="col-lg-2 col-md-3 col-sm-12 p-0">
                    <div class="input-group input-group-sm">
                        <input class="form-control form-control-sm" type="search" placeholder="接口名称">
                        <button class="btn btn-sm btn-outline-secondary" type="submit" title="搜索接口">搜索</button>
                    </div>

                    <div class="aside-menu skeleton">
                        <div class="accordion" id="asideMenuAcc">
                            <div class="accordion-item" v-for="(item, idx1) in apiDataSet" :key="idx1">
                                <h2 class="accordion-header" :id="'asideMenuAccHeading' + idx1">
                                    <button class="accordion-button" type="button" data-bs-toggle="collapse"
                                        :data-bs-target="'#asideMenuAccContent' + idx1" aria-expanded="true"
                                        :aria-controls="'asideMenuAccContent' + idx1" :title="item.title">
                                        {{ shortText(item.title,12) }}
                                    </button>
                                </h2>
                                <div :id="'asideMenuAccContent' + idx1" class="accordion-collapse collapse show"
                                    :aria-labelledby="'asideMenuAccHeading' + idx1">
                                    <div class="accordion-body p-0">
                                        <ul class="list-group list-group-flush">
                                            <li class="list-group-item" v-for="(item2, idx2) in item.children"
                                                :key="idx2">
                                                <a class="link-dark" href="#" @click.prevent="onChangeMenu(idx1,idx2)"
                                                    :title="item2.title">{{shortText(item2.title,12)}}</a>
                                            </li>
                                        </ul>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </aside>
                <!-- /aside -->
                <main class="col-lg-10 col-md-9 col-sm-12 py-2">
                    <section>
                        <h4>{{apiData.title}}</h4>
                        <div class="my-2 text-muted" v-html="apiData.summary"></div>
                        <h6>
                            <span>接口地址</span>
                            <a class="btn-toggle mx-3" href="#" title="展开或收起内容">&equiv;</a>
                            <a href="#top" title="回到顶部">&and;</a>
                        </h6>
                        <div class="input-group input-group-sm">
                            <select name="method" title="请求方式" v-model="apiData.method">
                                <option value="GET">GET</option>
                                <option value="POST">POST</option>
                                <option value="PUT">PUT</option>
                                <option value="DELETE">DELETE</option>
                                <option value="HEADER">HEADER</option>
                            </select>
                            <input name="url" type="text" class="form-control"
                                placeholder="请填写接口 URL（不含 QueryString 参数）" title="接口 URL" v-model="apiData.url">
                            <button class="btn btn-secondary btn-debug" type="button"
                                @click.prevent="onRequest()">发送请求</button>
                        </div>
                        <div class=" mb-3">
                            <details>
                                <summary>Resetful Api 示例</summary>
                                <p>
                                <table class="table table-sm table-bordered">
                                    <thead>
                                        <tr>
                                            <th>Verb</th>
                                            <th>URI</th>
                                            <th>Action</th>
                                            <th>Route Name</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <tr>
                                            <td>GET</td>
                                            <td><code>/photos/{photo}/comments</code></td>
                                            <td>index</td>
                                            <td>photos.comments.index</td>
                                        </tr>
                                        <tr>
                                            <td>GET</td>
                                            <td><code>/photos/{photo}/comments/create</code></td>
                                            <td>create</td>
                                            <td>photos.comments.create</td>
                                        </tr>
                                        <tr>
                                            <td>POST</td>
                                            <td><code>/photos/{photo}/comments</code></td>
                                            <td>store</td>
                                            <td>photos.comments.store</td>
                                        </tr>
                                        <tr>
                                            <td>GET</td>
                                            <td><code>/comments/{comment}</code></td>
                                            <td>show</td>
                                            <td>comments.show</td>
                                        </tr>
                                        <tr>
                                            <td>GET</td>
                                            <td><code>/comments/{comment}/edit</code></td>
                                            <td>edit</td>
                                            <td>comments.edit</td>
                                        </tr>
                                        <tr>
                                            <td>PUT/PATCH</td>
                                            <td><code>/comments/{comment}</code></td>
                                            <td>update</td>
                                            <td>comments.update</td>
                                        </tr>
                                        <tr>
                                            <td>DELETE</td>
                                            <td><code>/comments/{comment}</code></td>
                                            <td>destroy</td>
                                            <td>comments.destroy</td>
                                        </tr>
                                    </tbody>
                                </table>
                                参考：<a href="http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html" target="_blank">RESTful API 最佳实践</a>
                                </p>
                            </details>
                        </div>
                        <div>
                            <h6>
                                <span>QueryString 参数</span>
                                <a class="btn-toggle mx-3" href="#" title="展开或收起内容">&equiv;</a>
                                <a href="#top" title="回到顶部">&and;</a>
                            </h6>
                            <table class="table table-sm table-hover bg-light">
                                <thead>
                                    <tr>
                                        <th>参数名称</th>
                                        <th>参数值</th>
                                        <th>操作</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr v-for="(item,idx) of apiData.queryParams" :key="idx">
                                        <td>
                                            <input class="form-control form-control-sm" type="text" name="key"
                                                placeholder="参数名称" v-model="item.name" />
                                        </td>
                                        <td>
                                            <easy-input v-model="item.value" :item="item"></easy-input>
                                        </td>
                                        <td class="cell-operate">
                                            <button class="btn btn-sm btn-secondary btn-add-row me-1"
                                                @click.prevent="onAddParam('queryParam',idx)">添加</button>
                                            <button class="btn btn-sm btn-secondary btn-remove-row"
                                                @click.prevent="onRemoveParam('queryParam',idx)">删除</button>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                        <div class="my-3">
                            <h6>
                                <span>Body 参数</span>
                                <a class="btn-toggle mx-3" href="#" title="展开或收起内容">&equiv;</a>
                                <a href="#top" title="回到顶部">&and;</a>
                            </h6>
                            <table class="table table-sm table-hover bg-light">
                                <thead>
                                    <tr>
                                        <th>参数名称</th>
                                        <th>参数值</th>
                                        <th>操作</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr v-for="(item,idx) of apiData.formParams" :key="idx">
                                        <td>
                                            <input class="form-control form-control-sm" type="text" name="key"
                                                placeholder="参数名称" v-model="item.name" />
                                        </td>
                                        <td>
                                            <easy-input v-model="item.value" :item="item"></easy-input>
                                        </td>
                                        <td class="cell-operate">
                                            <button class="btn btn-sm btn-secondary btn-add-row me-1"
                                                @click.prevent="onAddParam('bodyParam',idx)">添加</button>
                                            <button class="btn btn-sm btn-secondary btn-remove-row"
                                                @click.prevent="onRemoveParam('bodyParam',idx)">删除</button>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                        <div class="my-3">
                            <h6>
                                <span>Header 参数</span>
                                <a class="btn-toggle mx-3" href="#" title="展开或收起内容">&equiv;</a>
                                <a href="#top" title="回到顶部">&and;</a>
                            </h6>
                            <table class="table table-sm table-hover bg-light">
                                <thead>
                                    <tr>
                                        <th>参数名称</th>
                                        <th>参数值</th>
                                        <th>操作</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr v-for="(item,idx) of apiData.headers" :key="idx">
                                        <td>
                                            <input class="form-control form-control-sm" type="text" name="key"
                                                v-model="item.name" placeholder="参数名称" />
                                        </td>
                                        <td>
                                            <easy-input v-model="item.value" :item="item"></easy-input>
                                        </td>
                                        <td class="cell-operate">
                                            <button class="btn btn-sm btn-secondary btn-add-row me-1"
                                                @click.prevent="onAddParam('headers',idx)">添加</button>
                                            <button class="btn btn-sm btn-secondary btn-remove-row"
                                                @click.prevent="onRemoveParam('headers',idx)">删除</button>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                        <div class="my-3">
                            <h6>
                                <span>响应消息</span>
                                <a class="btn-toggle mx-3" href="#" title="展开或收起内容">&equiv;</a>
                                <a href="#top" title="回到顶部">&and;</a>
                            </h6>
                            <div>
                                <div class="text-danger">
                                    建议：请打开浏览器控制台，以便观察更多信息。
                                    <div class="input-group input-group-sm">
                                        <span class="input-group-text">请求信息：</span>
                                        <input class="form-control form-control-sm text-muted" type="text"
                                            :value="debugQuery" placeholder="把所有的请求数据全部转换成QueryString，方便调试。" />
                                        <button class="btn btn-sm btn-secondary" @click="onCopyDebugQuery()"
                                            title="复制信息">复制</button>
                                        <a class="btn btn-sm btn-secondary" :href="debugQuery" target="_blank"
                                            title="新窗口打开">新窗口</a>
                                    </div>
                                </div>
                                返回值 Response Text 信息（<a href="https://www.json.cn/" target="_blank">JSON在线解析及格式化验证</a>）
                                <div class="url-info"></div>
                                <textarea name="response_text" class="form-control form-control-sm" rows="5"
                                    placeholder="请点击左上角的【发送请求】按钮获取实时响应消息" :value="apiData.result"></textarea>
                                返回 Response Headers 信息
                                <textarea name="response_headers" class="form-control form-control-sm" rows="4"
                                    placeholder="请点击左上角的【发送请求】按钮获取实时响应消息" :value="apiData.error"></textarea>
                            </div>
                        </div>
                        <div>
                            <h6>
                                <span>结果示例</span>
                                <a class="btn-toggle mx-3" href="#" title="展开或收起内容">&equiv;</a>
                                <a href="#top" title="回到顶部">&and;</a>
                            </h6>
                            <div>
                                返回成功消息
                                <div class="url-info"></div>
                                <textarea name="response_text" class="form-control form-control-sm" rows="5"
                                    placeholder="请点击左上角的【发送请求】按钮获取实时响应消息"
                                    :value="toStringify(apiData.successExample)"></textarea>
                                返回失败消息
                                <textarea name="response_headers" class="form-control form-control-sm" rows="4"
                                    placeholder="请点击左上角的【发送请求】按钮获取实时响应消息"
                                    :value="toStringify(apiData.failExample)"></textarea>
                            </div>
                        </div>
                    </section>
                </main>
            </div>
        </div>
    </div>

    <script title="系统配置（请勿随意修改）">
        // 应用配置
        const App = {
            data() {
                return {
                    // 当前接口数据
                    apiData: {
                        name: 'empty',
                        title: '自定义接口调试',
                        summary: '前有 Postman，后有 Postwoman， 这个就叫 PostMe！<br />如果本工具不能满足需求，推荐使用头部菜单的【推荐工具】跳转到第三方工具。',
                        url: '',
                        method: 'GET',
                        queryParams: [
                            { name: "", value: "" }
                        ],
                        formParams: [
                            { name: "", value: "" }
                        ],
                        headers: [
                            { name: "Authorization", value: "" }
                        ],
                        result: "",
                        error: "",
                        successExample: "{status:1, data:null}",
                        failExample: "{status:0, message:'', code:0}",

                    },
                    // 接口数据源
                    apiDataSet: [
                        {
                            title: "默认模块",
                            children: [
                                {
                                    "title": "获取公告数据",
                                    "summary": "",
                                    "method": "POST",
                                    "url": "/api/blindbox/get_notices",
                                    "queryParams": [],
                                    "formParams": [
                                        {
                                            "type": "int",
                                            "name": "n",
                                            "comment": "记录数量",
                                            "default": "20",
                                            "required": true
                                        }
                                    ],
                                    "headers": [

                                    ],
                                    "result": "",
                                    "error": "",
                                    "successExample": "{status:1, data:null}",
                                    "failExample": "{status:0, message:'', code:0}",
                                },
                                {
                                    name: 'admin.demo.login',
                                    title: '演示接口',
                                    summary: '接口的描述内容，支持 HTML 代码。',
                                    method: 'GET',
                                    url: '/index.php/api/login',
                                    queryParams: [
                                        {
                                            name: 'password',
                                            value: 'pwd123456',
                                            text: '密码',
                                            component: 'password',
                                        }, {
                                            name: 'timestamp',
                                            default: '', // 变量替换，支持 {time},{microtime}
                                            text: '时间戳',
                                            component: 'timestamp', // timestamp类型会出现刷新按钮
                                            getValue: function () {
                                                return new Date().getTime().toString();
                                            }
                                        },
                                        {
                                            name: 'gender',
                                            text: '性别（0=女,1=男,2=保密）',
                                            value: '1',
                                            component: 'select',
                                            data: [{
                                                value: '0',
                                                text: '女'
                                            }, {
                                                value: '1',
                                                text: '男'
                                            }, {
                                                value: '2',
                                                text: '保密'
                                            }],
                                        }, {
                                            name: 'extra',
                                            value: '{k1:1, k2:"测试", k3:null}',
                                            component: 'textarea',
                                            text: '扩展数据',
                                        },
                                    ],
                                    formParams: [
                                        {
                                            name: 'password',
                                            value: 'pwd123456',
                                            component: 'password',
                                            text: '密码',
                                        }, {
                                            name: 'timestamp',
                                            component: 'timestamp', // timestamp类型会出现刷新按钮
                                            text: '时间戳',
                                            value: '',
                                            getValue: function () {
                                                // return new Date().getTime(); // microtime
                                                return Math.round(new Date().getTime() / 1000); // timestamp
                                            }
                                        },],
                                    headers: [
                                        {
                                            name: 'Authorization',
                                            value: 'token1',
                                            text: '登录认证头部信息',
                                            component: 'select',
                                            data: [{ value: 'token1', text: '用户-1' }, { value: 'token2', text: '用户-2' },]
                                        }
                                    ],
                                    result: "",
                                    error: "",
                                    successExample: "{status:1, data:null}",
                                    failExample: "{status:0, message:'', code:0}",
                                }
                            ],
                        },
                    ],
                    // 基础URL
                    baseUrl: '',
                    // 把所有参数全部转为QueryString的完整请求地址
                    debugQuery: '',
                    // 服务器网址配置
                    servers: [
                        { name: 'remote', text: '远程服务器', url: 'http://www.baidu.com' },
                        { name: 'local', text: '本地服务器', url: 'http://localhost' },
                    ],
                    // 在线请求站点收藏
                    olrequestSites: [
                        { text: '站长工具-HTTP接口测试工具', url: 'http://tool.chinaz.com/tools/httptest.aspx?jdfwkey=2ugnf2' },
                        { text: 'SOJSON-HTTP模拟请求', url: 'https://www.sojson.com/http/test.html' },
                        { text: 'PostMan', url: 'https://www.postman.com/downloads/' },
                    ],
                    // 在线解析JSON站点收藏
                    oljsonSites: [
                        { text: 'Json.cn', url: 'https://www.json.cn/' },
                    ]
                }
            },
            methods: {
                // 初始化数据
                async initData() {
                    this.servers = getServerData();
                    this.apiDataSet = getApiData();
                },
                // 初始化视图
                initView() {
                    // 移除骨架屏
                    document.querySelectorAll('.skeleton').forEach(el => {
                        el.classList.remove("skeleton");
                    })
                    // 展开或收起面板（必须放到数据填充后）
                    document.querySelectorAll('legend .btn-toggle, .btn-group .btn-toggle, h6 .btn-toggle').forEach(el => {
                        el.addEventListener('click', function (evt) {
                            evt.preventDefault();
                            evt.target.parentNode.nextElementSibling.classList.toggle('d-none');
                        })
                    })
                },
                // 选择服务器
                onChangeServer(url) {
                    this.baseUrl = url;
                },
                // 发起请求
                async onRequest() {
                    let headers = {}, debugQueryStringData = {};
                    this.apiData.headers.forEach(item => {
                        headers[item.name] = (item.value || '');
                        debugQueryStringData[item.name] = (item.value || '');
                    })
                    let url = this.apiData.url;
                    if (url.indexOf('http') != 0) {
                        let baseUrl = this.baseUrl.trim();
                        if (baseUrl.length == 0) {
                            alert('请设置服务器地址（位于页面右上角）！\n请求地址格式错误：' + url);
                            return;
                        }
                        if (baseUrl.endsWith('/')) {
                            baseUrl = baseUrl.slice(0, -1);
                        }
                        if (!url.startsWith('/')) {
                            url = '/' + url;
                        }
                        url = baseUrl + url;
                    }
                    let option = {
                        method: this.apiData.method,
                        headers: headers,
                    }
                    switch (option.method) {
                        case 'GET':
                            let queryString = '';
                            this.apiData.queryParams.forEach(item => {
                                queryString += '&' + item.name + '=' + (item.value || '');
                            })
                            if (queryString.length > 0) {
                                queryString = queryString.substring(1);
                            }
                            url = url + (url.indexOf('?') > -1 ? '&' : '?') + queryString;
                            break;
                        case 'POST':
                        case 'PUT':
                        case 'DELETE':
                            let formData = new FormData();
                            this.apiData.formParams.forEach(item => {
                                formData.set(item.name, (item.value || ''));
                                debugQueryStringData[item.name] = (item.value || '');
                            })
                            option.body = formData;
                            break;
                        default:
                            break;
                    }

                    // 合并调试信息
                    let debugQueryString = '';
                    for (let k in debugQueryStringData) {
                        debugQueryString += '&' + k + '=' + (debugQueryStringData[k] || '');
                    }
                    if (debugQueryString.length > 0) {
                        debugQueryString = debugQueryString.substring(1);
                    }
                    console.log(url, debugQueryString);
                    this.debugQuery = url + (url.indexOf('?') > -1 ? '&' : '?') + debugQueryString;

                    console.log('发送数据：', url, option, this.apiData);
                    try {
                        let result = await fetch(url, option);
                        this.apiData.result = await result.text();
                        console.log('返回结果：', this.apiData.result);
                    } catch (error) {
                        console.log('返回错误：', error);
                        this.apiData.error = error.message;
                    }
                },
                // 添加一行参数
                onAddParam(name, idx) {
                    idx += 1;
                    switch (name) {
                        case 'queryParam':
                            this.apiData.queryParams.splice(idx, 0, { name: "", value: "" });
                            return
                        case "bodyParam":
                            this.apiData.formParams.splice(idx, 0, { name: "", value: "" });
                            return;
                        case 'headers':
                            this.apiData.headers.splice(idx, 0, { name: "", value: "" });
                            return;
                    }
                },
                // 移除一行参数
                onRemoveParam(name, idx) {
                    switch (name) {
                        case 'queryParam':
                            this.apiData.queryParams.splice(idx, 1);
                            return;
                        case "bodyParam":
                            this.apiData.formParams.splice(idx, 1);
                            return;
                        case 'headers':
                            this.apiData.headers.splice(idx, 1);
                            return;
                    }
                },
                // 点击侧边栏的菜单项目
                onChangeMenu(idx1, idx2) {
                    this.apiData = Object.assign({}, this.apiDataSet[idx1].children[idx2]);
                },
                // 复制请求信息
                onCopyDebugQuery() {
                    let elInput = document.createElement('input');
                    elInput.value = this.debugQuery;
                    document.body.appendChild(elInput);
                    elInput.select();
                    document.execCommand('Copy');
                    document.body.removeChild(elInput);
                },
                // 格式化数据
                toStringify(obj) {
                    return typeof (obj) == 'object' ? JSON.stringify(obj) : obj;
                },
                // 截短字符串
                shortText(text, length = 16) {
                    if (text.length <= length) {
                        return text;
                    }
                    let i = Math.floor(length / 2);
                    return text.substring(0, length - i) + ' ... ' + text.slice(-i);
                }
            },
            created() {
                this.initData();
            },
            mounted() {
                this.initView();
            }
        }
        // EasyInput 组件配置
        function getComponentOfEasyInput() {
            return {
                props: {
                    item: {
                        type: Object,
                        default: {}
                    }
                },
                data() {
                    return {
                        value: this.item.value

                    }
                },
                methods: {
                    onGetValue() {
                        if (typeof (this.item.getValue) == 'function') {
                            this.item.value = this.item.getValue();
                        } else {
                            alert('未设置 param.getValue() 方法。')
                        }
                    },
                    onShowPassword($event) {
                        let elInput = $event.target.previousElementSibling;
                        elInput.type = elInput.type == 'password' ? 'text' : 'password';
                    },
                    onGetTimestamp() {
                        // return new Date().getTime(); // microtime
                        this.item.value = Math.round(new Date().getTime() / 1000); // timestamp
                    }
                },
                template: `<div v-if="item.component=='timestamp'" class="input-group input-group-sm">
                <input class="form-control form-control-sm" type="search" name="value"
                    :placeholder="item.text||'参数值'" v-model="item.value" />
                <button class="btn btn-sm btn-secondary" @click.prevent="onGetTimestamp($event)">刷新</button>
            </div>
            <div v-else-if="item.component=='password'" class="input-group input-group-sm">
                <input class="form-control form-control-sm" type="password" name="value"
                :placeholder="item.text||'参数值'" v-model="item.value" />
                <button class="btn btn-sm btn-secondary" @click.prevent="onShowPassword($event)">显示</button>
            </div>
            <div v-else-if="item.component=='select'" class="input-group input-group-sm">
                <input class="form-control form-control-sm" type="search" name="value"
                    :placeholder="item.text||'参数值'" v-model="item.value" />
                <select class="form-select form-select-sm" v-model="item.value">
                    <option :value="item2.value" v-for="(item2,idx2) of item.data"
                        :key="idx2">{{item2.text}}</option>
                </select>
            </div>
            <div v-else-if="item.component=='textarea'">
                <textarea class="form-control form-control-sm" v-model="item.value"
                :placeholder="item.text||'参数值'"></textarea>
            </div>
            <div v-else>
                <input class="form-control form-control-sm" type="search" name="value"
                :placeholder="item.text||'参数值'" v-model="item.value" />
            </div>`
            }
        }
    </script>

    <script title="用户配置">
        // ===========================================================================
        //                     以下代码用于创建Vue应用（请勿修改代码）
        // ===========================================================================
        // 创建 Vue 应用
        var app = Vue.createApp(App);
        // 注册组件
        app.component('easy-input', getComponentOfEasyInput())
        // 加载根元素
        app.mount('#app')

        // ===========================================================================
        //                     以下代码用于配置用户真实数据源（可编辑代码）
        // ===========================================================================
        /**
         * 获取服务器网址数据（请勿修改函数名称）
         */
        function getServerData() {
            return [
                { name: 'remote', text: '远程服务器', url: 'http://127.0.0.1:8000' },
                { name: 'local', text: '本地服务器', url: 'http://localhost:8000' },
            ];
        }

        /**
         * 获取接口数据（请勿修改函数名称）
         */
        function getApiData() {
            // return __apidataset__;
        }
    </script>
</body>

</html>